今日進度3/14:
設定Azure AI Services(或 Azure Cognitive Services for Vision) 的OCR服務。
撰寫一個簡單的Python程式碼,使用一個營養標示圖片進行OCR服務呼叫,並印出返回的JSON結構,確認服務能正常運作。
一開始先在Vision Studio試用OCR服務,但不管是用筆電前鏡頭上傳或是直接上傳電腦裡的圖片,都會有辨識不出來或是數據錯誤的問題,或是繁中辨識錯誤(我的蜜棗乾怎麼變成蛋蛋乾😫)
看了JSON檔感覺很混亂,因為我沒用過OCR服務,我把一樣的照片丟去ChatGPT卻能解析正確,所以我問它這是正常的嗎?以下是它的回覆:
我這邊跟Azure OCR那種「電腦視覺」完全不一樣。
🔹我用到的技術其實是:圖像理解(Image Understanding):我能看懂圖片裡的文字和排版,像人類一樣「讀」出來,而不是逐像素辨識。
自然語言處理(NLP):我把圖裡的表格資訊轉換成結構化數據(例如「每份 218 大卡」),然後再進行計算。
邏輯運算:當你問「6份是多少」時,我就用簡單的乘法幫你算出答案。
換句話說,我沒有跑OCR,而是直接「看懂」圖片中的營養標示內容。這就是為什麼我不會遇到Azure OCR那種「數字亂跳、空值」的情況。
👉 所以可以理解成:
Azure OCR → 先看圖 → 轉成文字 → 你自己再整理數據。
我這邊 → 直接理解圖 → 自動幫你抓重點並算數字。
然後我請它推薦比OCR更適合我的專案的服務,它推薦了Azure Document Intelligence(前身叫 Form Recognizer),這是專門處理文件的服務,特別適合表格、發票、報表之類結構化文檔。它的優勢在於能識別文字之外,還能提取「欄位名稱 + 對應值」的 key-value 結構,非常方便輸出 JSON 格式,用於自動化流程。
然後我發現Gemini的Gem原來已經免費開放使用了,立刻開一個Gem來專門處理這個專案!
目前為止跟Gemini的協作都還算滿意,以下的流程都是在Gem裡面討論出來的:
👽=我
🫡=Gemini
👽我決定要改用Document Intelligence文件智慧服務,而非OCR!
🫡這個改動方向非常正確!既然舊的OCR辨識效果不佳,果斷換成更強大的Azure AI文件智慧服務是提升準確度的關鍵一步。它不只是單純的文字辨識,更擅長處理結構化的文件,這對我們擷取營養標示的表格數據來說是個大優勢。
舊的OCR服務可能更偏向於通用的文字辨識,對於不規則的字體、複雜的背景或表格格式表現不佳。但文件智慧服務專為處理文件而設計,它能:
👽接下來的開發計畫(第3天)
🫡好的,既然決定轉換技術,我們的開發計畫也需要調整。接下來的重點將是專注於整合文件智慧服務,並設計一個更穩固的數據處理流程。
👽那要去哪裡可以試用這個服務?
🫡Azure AI 文件智慧服務 (Document Intelligence) 有它專屬的視覺化工具,叫做 Document Intelligence Studio。它不像 Vision Studio 那樣是通用型的,而是專門為了處理文件、表格、表單等結構化資料而設計的。
在網站裡,你會看到幾個不同的模型可以試用,我們需要的是 「Read」 或 「Layout」 這兩個模型。
Read (閱讀):專門用來擷取高品質的列印和手寫文字。
Layout (版面配置):這個更強大,它不僅能擷取文字,還能同時辨識表格、標籤、選取標記等結構化資訊。
將營養標示的圖片上傳到「Layout」模型,你就能看到它回傳的結構化 JSON 數據,裡面會清楚標示出每個表格、行、列的資訊,這會比通用型 OCR 的純文字辨識結果更有用。
👽太好了!那我要用Layout功能,感覺超級聰明!
決定使用Latout後,就開啟VS Code來測試囉!
1. 終端機安裝 pip install azure-ai-documentintelligence
2. 初始化客戶端
3. 讀取圖片
4. 呼叫分析功能
5. 處理結果:API會回傳一個操作物件。需要等待分析完成,然後從結果中提取出我們需要的表格 (tables) 和內容 (content)。
6. 解析數據:從回傳的JSON結構化數據中,找到表格裡的「蛋白質」、「碳水化合物」等欄位,並取出它們對應的數值。
*但因為串接LINE BOT不是今天的進度,所以我先把程式碼設計成用tkinter可以呈現的(直接開啟視窗讓我上傳檔案)
最後再加入一個def函式extract_nutrition_data,以利從tables中提取這些數據:
def extract_nutrition_data(tables):
if not tables:
return None
# 假設營養標示總是在第一個表格
nutrition_table = tables[0]
data = {}
# 建立一個從營養素名稱到其在表格中行索引的對應表
nutrient_row_mapping = {
'熱量': 4,
'蛋白質': 5,
'脂肪': 6,
'碳水化合物': 9,
'糖': 10,
'鈉': 11
}
# 遍歷表格中的所有單元格
for cell in nutrition_table.cells:
# 尋找關鍵營養素的名稱
if cell.row_index in nutrient_row_mapping.values() and cell.column_index == 0:
nutrient_name = cell.content.replace(' ', '')
# 找到對應行的「每份」和「每100公克」數值
for other_cell in nutrition_table.cells:
if other_cell.row_index == cell.row_index and other_cell.column_index == 1:
data[f'{nutrient_name}_per_serving'] = other_cell.content
elif other_cell.row_index == cell.row_index and other_cell.column_index == 2:
data[f'{nutrient_name}_per_100g'] = other_cell.content
# 這裡可以加上數據清洗的邏輯,將字串轉成數字
cleaned_data = {}
for key, value in data.items():
# 移除單位,例如 '大卡', '公克', '毫克'
numeric_value = value.replace(' 大卡', '').replace(' 公克', '').replace(' 毫克', '').replace(' ', '')
try:
cleaned_data[key] = float(numeric_value)
except ValueError:
cleaned_data[key] = numeric_value # 如果無法轉換,保留原始字串
return cleaned_data
# 範例使用
# from the OCR result, get tables object
# extracted_data = extract_nutrition_data(result.tables)
# print(extracted_data)
{
"熱量_per_serving": 180.0,
"熱量_per_100g": 564.0,
"蛋白質_per_serving": 2.3,
"蛋白質_per_100g": 7.2,
"脂肪_per_serving": 11.2,
"脂肪_per_100g": 35.0,
"碳水化合物_per_serving": 17.6,
"碳水化合物_per_100g": 55.0,
"糖_per_serving": 1.2,
"糖_per_100g": 3.6,
"鈉_per_serving": 187.0,
"鈉_per_100g": 585.0
}
資訊很正確!試了用筆電前鏡頭也可以。請讓我讚嘆它的聰明
以上再融合昨天寫好的GRADIO版本程式碼,讓用戶輸入身高、體重、性別、吃了一包/半包/四分之一包:
👽我換了一個測試資料,結果遇到了「脂肪」辨識錯誤,正確是22.4克,但顯示0克,我猜是因為脂肪的下方還分為飽和脂肪跟反式脂肪,所以辨識錯誤?
🫡你說的沒錯,要解決這個問題,我們需要讓程式碼能更智慧地處理「脂肪」這個特殊情況。最簡單也最有效的方法是:
優先尋找「脂肪」這個關鍵字:這是我們的首選,如果找到了,就提取它的數值。
如果沒有找到「脂肪」:那麼退而求其次,將「飽和脂肪」和「反式脂肪」的數值相加,作為「總脂肪」。
# --- 針對脂肪的特殊處理 ---
total_fat_per_serving = cleaned_data.get('脂肪_per_serving_str', 0)
if total_fat_per_serving == 0:
# 如果總脂肪為0,嘗試將飽和脂肪和反式脂肪相加
saturated_fat = cleaned_data.get('飽和脂肪_per_serving_str', 0)
trans_fat = cleaned_data.get('反式脂肪_per_serving_str', 0)
total_fat_per_serving = saturated_fat + trans_fat
cleaned_data['脂肪_per_serving_str'] = total_fat_per_serving
return cleaned_data, None
這一版終於成功了!今天也修正了很多東西,超有趣的🤣
由於明天原訂進度於今天提前做完,故進度改為:
在LINE Developers後台設定BOT的基本資訊、撰寫基於 Python Flask 或 FastAPI 的簡單後端程式,使其能接收 LINE 傳來的文字訊息,並回覆「哈囉!」等基本文字。
加油!!